package org.stratusdb;

import java.io.*;
import java.net.*;
import java.sql.SQLException;

import org.stratusdb.metadata.Catalog;
import org.stratusdb.types.TableType;
import org.stratusdb.error.EnumError;
import org.stratusdb.error.Error;
import org.stratusdb.funsql.compile.FunSQLCompiler;
import org.stratusdb.funsql.statement.AbstractStatement;
import org.stratusdb.funsql.statement.SelectStmt;

/**
 * 
 * @author cbinnig
 *
 * TODO:
 * - AbstractStatement: compile - need to check for max. length of values
 */
class ServerThread extends Thread {
	private Socket client;
	private String sql;
	private SelectStmt se;
	
	public ServerThread(Socket client){
		this.client = client;
	}
	public void run() {
		while(!client.isClosed()) {
			handleStmt();
			while(this.se != null) {
				BufferedReader in;
				String com;
				try {
					in = new BufferedReader(new InputStreamReader(this.client.getInputStream()));
					com = in.readLine();
					handleMore(com);
				} catch (IOException e){
				//nothing
				}
			}
		}
	}
	
	public void handleMore(String com) throws IOException {
		System.out.println("entered handlemore");
		if(com.equalsIgnoreCase("a")){
			ObjectOutputStream os= new ObjectOutputStream(this.client.getOutputStream());
			TableType erg = this.se.getResult();
			System.out.println(erg);
			if(erg == null) {
				System.out.println("se");
				this.se = null;
			}
			os.writeObject(erg);
			os.flush();			
		}
		System.out.println("leaving handlemore");
	}
	
	public void handleStmt() {
		System.out.println("entered handlestmt");
		Error lastError = Error.NO_ERROR;
		AbstractStatement stmt = null;		
		try{
			BufferedReader in;
			try {
				in = new BufferedReader(new InputStreamReader(this.client.getInputStream()));
				this.sql = in.readLine();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println("Anfrage von "+ client.getInetAddress() +":" +client.getPort() + " um " + System.nanoTime() + " :\n\t" + this.sql);
			FunSQLCompiler compiler = new FunSQLCompiler();
			stmt = compiler.compile(this.sql);
			in = null;
			this.sql = null;
		
			lastError = compiler.getLastError();    
			if(lastError.isError()) {
				throw new SQLException(lastError.toString());
			}
		
			lastError = stmt.execute();
			if(lastError.isError()) {
				throw new SQLException(lastError.toString());
			}
			if(stmt instanceof SelectStmt) {
				this.se = (SelectStmt) stmt;
			}
		}
		catch(SQLException e) {
			System.out.println(e);
		}
		finally {
			ObjectOutputStream os;
			try {
				os = new ObjectOutputStream(this.client.getOutputStream());
				os.writeObject(lastError);
				os.flush();
				if(lastError.isError()) {
					this.se = null;
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				//e.printStackTrace();
			}		  
		}
		System.out.println("leaving handleStmt");
	}
}

public class Server {
	
	public static ServerSocket sock;
	
	public static Error start() {
		Error lastError;	

		try {	
			sock = new ServerSocket(Config.PORT);
		} catch (IOException e) {
			String[] msg = {""+Config.PORT};
			return new Error(EnumError.PORT_NOT_AVAILABLE,msg);
		}
		
		//init catalog from database
		lastError = Catalog.load();
		
		
		return lastError;
	}
		
	public static void main(String[] args) throws Exception {
		Error lastError = start();
		
		if(lastError.isError()) {
			System.out.println(lastError);
			return;
		}
		System.out.println("Server started @"+Config.PORT);
		while(true) {
			Socket client = sock.accept();
			ServerThread st = new ServerThread(client);
			st.start();		    
		}
	}
}
